﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;

namespace EveMarketsDAO.MiseAjourBase
{
    public class MiseAJourMarkets
    {
        /*
         * - Récupérer la dernière date de mise à jour
         * - Télécharger tous les fichiers depuis Eve-central
         * - Décompresser les fichiers
         * - Mettre en forme chaque fichier
         * - Regrouper tous les fichiers dans 1 seul
         * Appeller les méthodes des procédures stockés
         */

    	public readonly List<string> ListDesDatesDl;
    	private string RepertoireTemp { get; set; }

		#region Constructeur
		
		/// <summary>
		/// Constructeur de la classe.
		/// </summary>
		public MiseAJourMarkets(DateTime aujourdhui, string pathTemp, DateTime derniereDate)
		{
			// Je vais chercher la derniere date de la mise à jour, et la "transforme" en une liste de fichier
			// potentiel à télécharger.
			RepertoireTemp = pathTemp;
			ListDesDatesDl = CreerLaListe(aujourdhui, derniereDate);
		} 

		public MiseAJourMarkets(DateTime aujourdhui, string pathTemp)
		{
			RepertoireTemp = pathTemp;
			ListDesDatesDl = CreerLaListe(aujourdhui, aujourdhui.AddDays(-2));
		}

		#endregion

		#region Méthode public

		/// <summary>
		/// Cette méthode permet de mettre à jour le Market
		/// SANS mettre à jour les compteurs pour les statistiques.
		/// </summary>
		/// <returns>Retourne le nombre de ligne mise à jours</returns>
		public int UpdateLaBaseMarket(BackgroundWorker worker)
		{
			int nbreLigneMerge = 0;
			int percentComplete = 0;

			// Pourcentage de UN fichier à traiter.
			var pctUnFichier = 100/ListDesDatesDl.Count;

			if (!Directory.Exists(this.RepertoireTemp))
				Directory.CreateDirectory(this.RepertoireTemp);

			// Je dois traiter fichier par fichier, et non plus un global.
			foreach (var dateAFaire in ListDesDatesDl)
			{
				// Téléchargement du fichier et report de progression
				StartDownload(dateAFaire, this.RepertoireTemp);
				worker.ReportProgress(percentComplete += (pctUnFichier/7) , @"Téléchargement du fichier : " + dateAFaire);

				// Décompression du fichier et report de progression
				var fichierDecompresse = DecompresseFichier(this.RepertoireTemp + "\\" + dateAFaire);
				if (fichierDecompresse != "probleme")
				{
					worker.ReportProgress(percentComplete += (pctUnFichier/7), @"Décompression du fichier : " + dateAFaire);

					// Mettre en forme le fichier et report de progression
					bool convertionOk = MiseEnFormeFichiers(fichierDecompresse);
					worker.ReportProgress(percentComplete += (pctUnFichier/7), @"Mise en forme du fichier : " + fichierDecompresse);

					// Insérer le fichier avec tout le markets dans la table Temporaire.
					var nbreligneInsere = ContexteDAO.ContexteDonnees.InsertTable();
					worker.ReportProgress(percentComplete += (pctUnFichier/7),
					                      @"Insertion temporaire : " + nbreligneInsere + " ligne(s) insérées.");

					// Faire le Merge entre les 2 tables.
					nbreLigneMerge = ContexteDAO.ContexteDonnees.MergeTempToMarkets(RemiseEnDate(dateAFaire));
					worker.ReportProgress(percentComplete += (pctUnFichier/7), @"Merge de " + nbreLigneMerge + " ligne(s).");

					// Supprimer la table Temporaire et mise à jour du compteur.
					var nbreLigneDelete = ContexteDAO.ContexteDonnees.DeleteTemp();
					worker.ReportProgress(percentComplete += (pctUnFichier/7), @"Suppression de " + nbreLigneDelete + " ligne(s).");
				}
				else
				{
					worker.ReportProgress(percentComplete = (percentComplete + (pctUnFichier / 7)) * 5, @"Décompression du fichier : " + dateAFaire);
				}

				SuppressionFichiers(this.RepertoireTemp);
				worker.ReportProgress(percentComplete += (pctUnFichier / 7), @"Suppression des fichiers.");
			}

			// Mise à jour pour la ProgressBar
			worker.ReportProgress(100, @"Traitement fini");

			ContexteDAO.ContexteDonnees.UpdateTable(TimeZoneInfo.ConvertTimeToUtc(DateTime.Now));

			return nbreLigneMerge;
		}

		private DateTime? RemiseEnDate(string dateAFaire)
		{
			// Exemple de string en paramètre : 2011-08-28.dump.gz
			char[] separateur = new char[]{'-','.'};
			var separationBrut = dateAFaire.Split(separateur);
			int annee = Convert.ToInt32(separationBrut[0]);
			int mois = Convert.ToInt32(separationBrut[1]);
			int jour = Convert.ToInt32(separationBrut[2]);

			return new DateTime(annee,mois,jour);
		}

		#endregion

		#region Méthodes private.
		
		/// <summary>
		/// Supprime tout les fichiers se trouvant dans le répertoire.
		/// </summary>
		/// <param name="repertoireTemp"></param>
		private void SuppressionFichiers(string repertoireTemp)
		{
			DirectoryInfo di = new DirectoryInfo(repertoireTemp);
			var fichiers = di.GetFiles();
			foreach (var fichier in fichiers)
			{
				fichier.Delete();
			}
		}

		/// <summary>
		/// Décompresse les fichiers dans l'emplacement passé en paramètre
		/// </summary>
		/// <param name="nomDuFichier">Chemin ou décompresser les fichiers.</param>
		/// <returns></returns>
		private static string DecompresseFichier(string nomDuFichier)
		{
			try
			{
				// Lecture du fichier compressé.
				StreamReader streamReader = new StreamReader(nomDuFichier);
				
				// Création du fichier sortie (fichier décompressé).
				using (FileStream outFile = File.Create(nomDuFichier + ".csv"))
				{
					using (GZipStream gzip = new GZipStream(streamReader.BaseStream, CompressionMode.Decompress))
					{
						gzip.CopyTo(outFile);
					}
				}
			}
			catch (Exception ex)
			{
				// Mettre le Log ici.
				return "probleme";
				throw;
			}

			return nomDuFichier + ".csv";
		}

		/// <summary>
		/// Lance le téléchargement
		/// </summary>
		/// <param name="fichier">Fichier à télécharger</param>
		/// <param name="pathPourTemp">Répertoire dans lequel est sauvegarder les fichiers.</param>
		private static void StartDownload(string fichier, string pathPourTemp)
		{
			const string CHEMIN_EVE_CENTRAL = "http://www.eve-central.com/dumps/";
			// Création d'un objet WebClient (c'est lui qui permet de télécharger un fichier)
			WebClient wc = new WebClient();

			try
			{
				// Lancement du téléchargement
				// argument 1 : url du fichier source
				// argument 2 : l'emplacement ou enregistrer le fichier sur le disque dur
				wc.DownloadFile(CHEMIN_EVE_CENTRAL + fichier, pathPourTemp + "\\" + fichier);
			}
			catch (Exception e)
			{
				// Destruction de l'objet WebClient
				wc.Dispose();
			}
			finally
			{
				// Destruction de l'objet WebClient
				wc.Dispose();
			}
		}

		/// <summary>
		/// Elle permet de créer une liste de fichier à télécharger, par rapport à la date
		/// passé en paramètre, et la date du jour (aussi en paramètre)
		/// </summary>
		/// <param name="aujourdhui">Date du jour</param>
		/// <param name="derniereDate">Date prise dans la base de donnée</param>
		/// <returns>Liste de fichier à téléchargé</returns>
		private static List<string> CreerLaListe(DateTime aujourdhui, DateTime derniereDate)
		{
			// Forme à avoir : 2011-08-28.dump.gz
			List<string> retourList = new List<string>();

			// Là, je vais chercher la différence entre les 2 dates.
			TimeSpan ts = aujourdhui.Date - derniereDate.Date;

			// Pour chaque jour, je vais mettre en forme un fichier.
			for (int i = 0; i <= ts.Days - 1; i++)
			{
				var dateM = MiseEnFormeUneDate(derniereDate.AddDays(i));
				retourList.Add(dateM);
			}

			return retourList;
		}

		/// <summary>
		/// Transforme une date (DateTime) en forme string voulu.
		/// </summary>
		/// <param name="date">date à mettre en forme.</param>
		/// <returns>Date sous forme de string : 2011-08-28.dump.gz</returns>
		private static string MiseEnFormeUneDate(DateTime date)
		{
			// Exemple de forme à avoir : 2011-08-28.dump.gz
			StringBuilder sb = new StringBuilder(6);
			sb.Append(date.Year).Append("-").Append(date.Month.ToString("D2")).Append("-").Append(date.Day.ToString("D2")).Append(".dump.gz");

			return sb.ToString();
		}

		/// <summary>
		/// Enlève le surplus de charactère non utils, voir génant pour l'insertion en base.
		/// </summary>
		/// <param name="bonFichier">Liste de fichier à mettre en forme.</param>
		private bool MiseEnFormeFichiers(string bonFichier)
		{
			/* Exemple de ligne.
			 * "orderid","regionid","systemid","stationid","typeid","bid","price","minvolume","volremain","volenter","issued","duration","range","reportedby","reportedtime"
			 * "2201857300","10000002","30000169","60003208","12084","0","4200000.0","1","9","9","2011-06-23 14:06:36","90 days, 0:00:00","32767","0","2011-08-26 06:46:53.654991"
			 * 
			 * Il faut avoir :
			 * 2201857300,10000002,30000169,60003208,12084,0,4200000.0,1,9,9,2011-06-23 14:06:36,90,32767,0,2011-08-26 06:46:53.654991
			 * 
			 */

			try
			{
				// Découper le flux.
				var fichierDecoupe = File.ReadAllLines(bonFichier).ToList();
//					fichier.Split(new string[] {"\n\r"}, StringSplitOptions.RemoveEmptyEntries).ToList();

				// Faire sauter la première et la dernière ligne.
				fichierDecoupe.RemoveAt(fichierDecoupe.Count - 1);
				fichierDecoupe.RemoveAt(0);

				var leGros = new StringBuilder();

				// Enlever les "", ' days, 0:00:00' et ' day, 0:00:00'
				for (int i = 0; i < fichierDecoupe.Count; i++)
				{
					StringBuilder sb = new StringBuilder(fichierDecoupe[i]);
					sb.Replace(" days, 0:00:00", string.Empty);
					sb.Replace(" day, 0:00:00", string.Empty);
					sb.Replace(@"""", string.Empty);
					sb.Append('|');

					leGros.Append(sb.ToString());
					sb.Clear();
				}

				File.AppendAllText(this.RepertoireTemp + @"\GoodFile.csv", leGros.ToString());
				leGros.Clear();
			}
			catch (Exception ex)
			{
				// Mettre ici le Log
				return false;
			}

			return true;
		}

    	#endregion
    }
}
